package umg.os.devices.filesystem.basic;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.InputMismatchException;
import java.util.List;

import umg.os.devices.DataManipulationUtilities;

public class HDDHandler {
	
	private Path hd;
	
	public static final long 
	DISK_SIZE = 300*1024*1024;  //300 MB
	
	private RandomAccessFile diskAccess;
	private boolean open;
	
	public HDDHandler(Path hd) {
		// TODO Auto-generated constructor stub
		this.hd=hd; 
		open = false;
	}
		
	protected List<Integer> getLinkedAddress(int address)
	{
		if(address%512!=0) {
			throw new InputMismatchException("Invalid memmory address");
		}
		byte[] nextAddressAsBytes = new byte[3];
		ArrayList<Integer> linkedAddress = new ArrayList<Integer>();
		try (RandomAccessFile raf= new RandomAccessFile(hd.toFile(),"rwd")) {
			do 
			{
				linkedAddress.add(address);
				raf.seek(address+BasicFileSystem.USEFUL_SECTOR_SIZE);
				raf.read(nextAddressAsBytes, 0, 3);
			}while((address = DataManipulationUtilities.intFrom3bytes(nextAddressAsBytes)) != 0);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return linkedAddress;
	}
	
	/*protected byte[] readFromLinkedSectors(int initialAddress) {
		if(initialAddress%512!=0) {
			throw new InputMismatchException("Invalid memmory address");
		}
		int actualAddress=initialAddress,nextAddress;
		List<Integer> fileAddress = new ArrayList<>();
		fileAddress.add(initialAddress);
		byte[] data = null;
		try (RandomAccessFile raf= new RandomAccessFile(hd.toFile(),"rwd")) {
			while(true) {
				actualAddress = actualAddress+BasicFileSystem.USEFUL_SECTOR_SIZE;
				raf.seek(actualAddress);
				byte[] nextAddressAsBytes = new byte[3];
				raf.read(nextAddressAsBytes, 0, 3);
				nextAddress = DataManipulationUtilities.intFrom3bytes(nextAddressAsBytes);
				if(nextAddress == 0) {
					break;
				}
				fileAddress.add(nextAddress);
			}
			data = new byte[fileAddress.size()*BasicFileSystem.USEFUL_SECTOR_SIZE];

			for(Integer i : fileAddress)
			{
				raf.seek(i);
				raf.read(data, 0, BasicFileSystem.USEFUL_SECTOR_SIZE);
			}
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
			System.err.println(e.getMessage());
			return null;
			
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
			System.err.println(e.getMessage());
			return null;
		}
		return data;
	}*/
	
	protected byte[] readFromLinkedSectors(int initialAddress) {
		if(initialAddress%512!=0) {
			throw new InputMismatchException("Invalid memmory address");
		}
		List<Integer> fileAddress = getLinkedAddress(initialAddress);
		byte[] data = new byte[fileAddress.size()*BasicFileSystem.USEFUL_SECTOR_SIZE];
		try (RandomAccessFile raf= new RandomAccessFile(hd.toFile(),"rwd")) {
			int sectorsRead = 0;
			for(Integer i : fileAddress)
			{
				raf.seek(i);
				raf.read(data, sectorsRead * BasicFileSystem.USEFUL_SECTOR_SIZE, BasicFileSystem.USEFUL_SECTOR_SIZE);
				sectorsRead++;
			}
		} 
		catch (FileNotFoundException e) 
		{
			e.printStackTrace();
			System.err.println(e.getMessage());
			return null;
			
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
			System.err.println(e.getMessage());
			return null;
		}
		return data;
	}
	
	protected boolean saveData(int initialAddress, byte[] data) throws IOException, VolumeUnavailableException {
		if (!open) {
			throw new VolumeUnavailableException();
		}
		diskAccess.seek(initialAddress);
		diskAccess.write(data);
		return true;
	}
	
	protected byte[] readData(int initialAddress, int sizeOfDataToRead) throws IOException, VolumeUnavailableException {
		if (!open) {
			throw new VolumeUnavailableException();
		}
		byte[] dataRead = new byte[sizeOfDataToRead];
		diskAccess.seek(initialAddress);
		diskAccess.read(dataRead);
		return dataRead;
	}
	
	public static void createDisc(Path hd) throws IOException {
		if (!Files.exists(hd.getParent())) {
			Files.createDirectories(hd.getParent());
		}
		System.out.println("Creating Disk...");
		try (RandomAccessFile raf = new RandomAccessFile(hd.toFile(), "rwd")) {
			/**
			 * TODO: change this to {@link #DISK_SIZE}
			 */
			raf.setLength(100*1024); // 100 Kb 	
			raf.close();
		}
		System.out.println("Disk successfully created...");
	}
	
	protected boolean isOpen() {
		return open && diskAccess != null; 
	}
	
	protected void open() throws FileNotFoundException {
		diskAccess = new RandomAccessFile(hd.toFile(), "rwd");
		open = true;
	}
	
	protected void close() throws IOException {
		if (diskAccess != null) {
			diskAccess.close();
		}
		diskAccess = null;
		open = false;
	}

	@SuppressWarnings("serial")
	public class VolumeUnavailableException extends Exception {
		
		private VolumeUnavailableException() {
			
		}
		
	}
	
}